home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource3
/
134_01
/
cprofile.c
< prev
next >
Wrap
Text File
|
1985-08-21
|
19KB
|
765 lines
/* CPROFILE -- BDS `C' Program Profiler */
/* Copyright (c) 1983 by Kevin B. Kenny
Released to the BDS `C' Users' Group for non-commercial distribution.
Kevin Kenny
729-A E. Cochise Dr.
Phoenix, Arizona 85020
*/
#define TITLE "CPROFILE version 83-11-14 copyright (c) 1983 by Kevin Kenny\n"
#include "bdscio.h"
#include "dio.h"
#include "cmdutil.h"
/* Configuration */
#define TOP 0x7000 /* End of usable memory. Must be supplied
to CLINK or L2 with the '-t' option. */
/* Define some general-purpose pointers */
union ptr {
unsigned i; /* Integer representation */
unsigned * w; /* Word pointer */
char * b; /* Byte pointer */
};
/* Define a long integer for the LONG.C package. */
struct longint {
char longcont [4];
};
#define LONG struct longint /* Poor man's TYPEDEF */
/* Define the structure of the execution profile table */
struct proftab {
unsigned pfaddr; /* PC of the RST 6 operation */
LONG pftimes; /* Number of times that RST was hit. */
} * ptent;
/* Define the structure of the beginning of the run-time RST 6
interceptor/supervisor */
struct supvsr {
char supjmp0; /* A JMP (0xC3) instruction */
unsigned supbdos; /* Entry to CP/M BDOS (where the JMP goes) */
char supname [9]; /* 'cprofile' monogram to detect re-entry */
char supjmp1; /* Another JMP (0xC3) instruction */
unsigned supinit; /* Initialization entry into supervisor */
char supjmp2; /* Another JMP (0xC3) instruction */
unsigned supboot; /* Fake warmboot entry into supervisor */
unsigned suptop; /* Upper address of the supervisor */
struct proftab * supent1; /* Pointer to first entry in profile */
struct proftab * supnxte; /* Pointer to next avail entry */
unsigned supbios; /* Address to restore to BIOS transfer */
char supcmd [128]; /* CPROFILE command line saved for reload */
};
union {
struct supvsr * sps; /* Pointer to the supervisor */
void (* spf) ();
unsigned spi; /* Integer value thereof */
} suprvp;
/* Define the command line options */
struct option optable [5]; /* Place to put the option table */
int tabsize; /* Size of the profile table needed */
int pagelen; /* Printer page length */
int nsrcfs; /* Number of source files supplied */
char * * sfiles; /* Names of the source files */
char * srcfnptr [2]; /* Default source file pointer */
char srcfnam [20]; /* Default source file */
/* Define the I/O environment */
char * prgnam; /* Name of the program being analyzed*/
char comfnam [20]; /* Name of the .COM file */
char comfile [134]; /* I/O stream for the .COM file */
char symfnam [20]; /* Name of the .SYM file */
FILE symfile; /* CHARIO stream for the .SYM file */
FILE srcfile; /* Input stream for the source file */
/* Working storage */
int hisargc; /* ARGC of profiled program */
char ** hisargv; /* ARGV of profiled program */
char pass2; /* Flag = TRUE iff in analysis pass */
char fnname [9]; /* Name of function being analyzed */
int stmt; /* Statement number within function */
int line; /* Line number within function */
char symfnnam [9]; /* Last function read from .SYM */
unsigned symtopad; /* Upper address of last .SYM funct */
char symend; /* Flag == TRUE if at end of .SYM */
char sfopen; /* Flag == TRUE if a source file open*/
char seof; /* FLAG == TRUE at end of source file*/
int slevel; /* Depth of {} nesting in source */
char sname [33]; /* Name of a symbol in source */
char infunct; /* Flag == TRUE if in funct in src */
int sline; /* Relative line # in source funct. */
char slopen; /* Flag == TRUE if in source line */
int linage; /* Number of lines on current page */
int pageno; /* Current page # */
/* Main program */
main (argc, argv)
int argc;
char * * argv;
{
union ptr bdosp;
/* Test if CPROFILE is active. If it is, this must be the second pass.
*/
pass2 = FALSE;
prgnam = argv [1]; /* Rem,ember the program name */
bdosp.i = 6; /* Point to the BDOS entry address in lowcore*/
suprvp.spi = *bdosp.w; /* Get the BDOS entry address (top of TPA) */
if (!strcmp (suprvp.sps -> supname, "cprofile")) pass2 = TRUE;
/* Scan the command line, separating CPROFILE's arguments from
the target program's. */
hisargv = argv = argv + 1; hisargc = 0; /* His args start at once */
--argc;
while (argc && strcmp (*argv, ":")) { /* My args start with a ":" */
++argv; --argc; ++hisargc;
}
if (pass2) dioinit (&argc, argv);
/* Process command line options */
getopts (argc, argv);
/* Call appropriate analysis routines */
if (pass2) {
/* Second pass -- call the analyzer */
analyze (argc, argv);
}
else {
/* First pass -- install supervisor and call in program to
be profiled */
if (!hisargc) {
fprintf (STD_ERR, "Command must be supplied.\n");
goto synerr;
}
fprintf (STD_ERR, TITLE);
install ();
fprintf (STD_ERR, "<<< Loading target program. >>>\n");
chain (hisargc, hisargv, &(suprvp.sps -> supjmp1));
}
synerr:
dioflush ();
}
/* Process command line options */
getopts (argc, argv)
int argc;
char ** argv;
{
char error; /* Flag = TRUE iff command erroneous */
union {
int ii;
char * si;
char * * msi;
} info;
/* Define the available options */
initv (optable, EOF,
#define STRG_OPT -1
"Linecount", NVAL_KWD,
#define LCNT_OPT 0
"PageLength", NVAL_KWD,
#define PLEN_OPT 1
"SourceFiles", MSVL_KWD,
#define SFIL_OPT 2
EOF);
++argv; --argc; /* Discard colon separator */
error = FALSE;
tabsize = 512 * sizeof ptent;
pagelen = 58;
srcfnptr [1] = & srcfnam;
strcpy (srcfnam, hisargv [0]);
makeext (srcfnam, "C");
sfiles = & srcfnptr;
nsrcfs = 1;
while (argc > 0) {
switch (procarg (&argc, &argv, optable, &info)) {
case STRG_OPT:
break; /* Discard redirection */
case LCNT_OPT:
tabsize = info.ii * sizeof (* ptent);
break;
case PLEN_OPT:
pagelen = info.ii;
break;
case SFIL_OPT:
nsrcfs = * (sfiles = info.msi);
break;
default:
error=TRUE;
}
}
if (error || hisargc < 1) {
showsyntax ("cprofile <command and options> : [>filename]",
optable);
quit ();
}
}
/* Install CPROFILE in the top of the TPA */
install () {
struct ptr p, q; /* Working pointer */
void superv (); /* The supervisor's code */
unsigned * supervrl (); /* The supervisor's relocation table */
unsigned suprvs; /* Size of the supervisor */
unsigned offset; /* Relocation factor for supervisor */
unsigned nrelocs; /* Number of relocated words in sup */
/* Find the top of the TPA. Using this, the size of the supervisor,
and the size of the profile table, find where the supervisor origin
will be. */
p.i = 6; /* Get address of BDOS jump + 1 */
p.i = *p.w; /* Get address of TPA top. */
suprvp.spf = &superv; /* Point to current supervisor origin*/
suprvs = suprvp.sps -> suptop - suprvp.spi;
/* Get its length */
p.i = (p.i - suprvs - tabsize) & 0xFF00;
/* Find new supervisor origin */
if (p.i < TOP) {
fprintf (STD_ERR,
"Profile table overlays CPROFILE; use smaller -L\n");
quit ();
}
/* Move the supervisor into place */
offset = p.i - suprvp.spi;
movmem (suprvp.spi, p.i, suprvs);
suprvp.spi = p.i;
/* Relocate the supervisor */
p.w = supervrl (); /* Get relocation table */
nrelocs = *p.w++;
while (nrelocs--) {
q.i = suprvp.spi + *p.w++; /* Get a relocation address */
*q.w += offset; /* Relocate it */
}
/* Link the BDOS transfer through the supervisor */
p.i = 6;
suprvp.sps -> supbdos = *p.w;
*p.w = suprvp.spi;
/* Link the BIOS warmboot transfer through the supervisor */
p.i = 1;
p.i = *p.w + 1;
suprvp.sps -> supbios = *p.w;
*p.w = &(suprvp.sps -> supjmp2);
/* Record the CPROFILE command line in the supervisor for reload*/
movmem (0x0080, &(suprvp.sps -> supcmd), 128);
}
/* Analyze the results of a profiled run */
analyze (argc, argv)
int argc;
char ** argv;
{
int pfcomp ();
struct proftab * tent;
/* Restore linkage to real BDOS */
deinstal ();
fprintf (STD_ERR, "<<< Starting analysis phase. >>>\n");
/* Open needed files for analysis */
openfiles ();
/* Initialize source file reader */
seof = infunct = sfopen = FALSE;
slevel = sline = 0;
/* Sort the profile data by address */
qsort (suprvp.sps -> supent1,
suprvp.sps -> supnxte - suprvp.sps -> supent1,
sizeof * tent,
pfcomp);
/* Display the profile */
sopen (); /* Open up the first source file */
linage = 0; pageno = 0; begline (); slopen = FALSE;
for (tent = suprvp.sps -> supent1;
tent < suprvp.sps -> supnxte;
++tent) {
doprent (tent);
}
}
/* De-install the run-time supervisor */
deinstal () {
union ptr p; /* Working pointer */
p.i = 6; /* Set pointer to BDOS entry point */
suprvp.spi = *p.w; /* Find supervisor entry */
*p.w = suprvp.sps -> supbdos; /* Relink the BDOS transfer correctly*/
}
/* Open .COM and .SYM files in analysis pass */
openfiles () {
/* Open the .SYM file */
strcpy (symfnam, prgnam);
makeext (symfnam, "SYM");
if (fopen (symfnam, symfile) == ERROR) {
fprintf (STD_ERR, "Can't open %s: %s\n",
symfnam, errmsg (errno ()));
quit ();
}
strcpy (symfnnam, "C.CCC");
if (frdstr (symfile, symfnnam) == ERROR) {
fprintf (STD_ERR, "Enpty .SYM file %s.\n", symfnam);
symend=TRUE;
symtopad = 0xFFFF;
}
/* Open the .COM file */
strcpy (comfnam, prgnam);
makeext (comfnam, "COM");
if (copen (comfile, comfnam) == ERROR) {
fprintf (STD_ERR, "Can't open %s: %s\n",
comfnam, errmsg (errno ()));
quit ();
}
}
/* Display one profile entry */
doprent (tent)
struct proftab * tent;
{
char tcount [12]; /* Text for count of times executed */
tcseek (comfile, tent -> pfaddr - 0x0100, 0, comfnam);
/* Read the RST 6 parameter */
tcread (comfile, &line, 2, comfnam);
stmt = line >> 12; line &= 0x0FFF; /* Get line number */
if (line > 1) slineno (line - 1);
if (line == 0) {
begfun (tent -> pfaddr); /* Begin a new function */
begline ();
printf ("%-8s", fnname);
}
else {
begline ();
printf ("%4d", line);
if (stmt == 0)
printf (" "); /* Print line # . stmt count */
else
printf (".%-2d\t", stmt);
}
ltoa (tcount, tent -> pftimes);
printf ("%7s\t", tcount);
slineno (line); /* Print source line */
}
/* Begin a new function in profile */
begfun (addr)
unsigned addr; /* Starting address of function + 1 */
{
while (addr >= symtopad && !symend) {
nxfsym ();
}
strcpy (fnname, symfnnam);
sfunct (fnname);
}
/* Compare addresses of two entries in the profile table */
int pfcomp (t1, t2)
struct proftab *t1, *t2;
{
if (t1 -> pfaddr > t2 -> pfaddr) return (1);
else if (t1 -> pfaddr == t2 -> pfaddr) return (0);
else return (-1);
}
/* Read the next function from the .SYM file */
nxfsym () {
if (symend) return;
if (frdstr (symfile, symfnnam) == ERROR
|| frdhex (symfile, &symtopad) == ERROR) {
symend = TRUE;
symtopad = 0xFFFF;
fclose (symfile);
}
}
/* Locate a function on the source file. Function is presumed to
start when its name appears outside curly-braces. */
sfunct (s)
char * s; /* Function name */
{
do {
if (seof) return; /* End of file; no hope. */
sscan(); /* Look for the name. */
} while (slevel != 0 || strcmp (sname, s));
infunct = TRUE;
sline = 1; /* This is first line of function. */
while (slevel == 0 && !seof) sscan (); /* Find the open brace */
seol (); /* Advance to a line break. */
}
/* Advance to the end of a function by balancing curly-braces. */
sendfn () {
infunct = FALSE;
while (slevel > 0 && !seof) sscan (); /* Eat tokens 'til balanced. */
seol (); /* Advance to a line break. */
}
/* Locate a line within the current function. */
slineno (l)
int l; /* Line number to find. */
{
while (sline != 0 && sline <= l) { /* Advance */
sscan ();
seol ();
}
}
/* Advance source file to a line break */
seol () {
while (slopen) sscan ();
}
/* Scan a token out of the source file */
sscan () {
int c; /* Current character from file */
c = sgetc (); /* Get a character */
if (isalpha (c) || c == '_')
ssymb (c); /* Scan an identifier if that's what we have */
else {
sputc (c); /* Flush the current character to output */
switch (c) {
case '/': scomt (); /* Process comments */
break;
case '{': ++slevel; /* Balance curly braces */
break;
case '}': --slevel; /* NO BREAK HERE */
case ';': if (!infunct) sline = 0; /* Reset relative line
when leaving function*/
break;
case '\'':
case '"': squote (c); /* Process quoted strings */
break;
case '\n': if (sline) ++sline; /* Maintain relative line # */
break;
case CPMEOF:
case EOF: seof = TRUE; /* Handle EOF */
slopen = FALSE;
sline = 0;
break;
}
}
}
/* Read an identifier from source file */
ssymb (c)
int c;
{
char * symp; /* Pointer to symbol being scanned */
symp = & sname; /* Initialize scan pointer */
do {
if (slevel == 0) *symp++ = toupper (c);
/* Accumulate character */
sputc (c);
c = sgetc (); /* Get next character */
} while (isalpha(c) || isdigit (c) || c == '_');
ungetc (c, srcfile); /* Push back char that stopped scan */
if (slevel == 0) *symp++ = '\0'; /* End the symbol */
sname [8] = '\0'; /* Truncate it to 8 bytes */
}
/* Read a quoted string from source file */
squote (c)
int c;
{
int c2;
c2 = c;
do {
if (c2 == '\\') sputc (sgetc ()); /* Do escapes */
c2 = sputc (sgetc ()); /* Next char */
} while (c2 != c && c2 != CPMEOF && c2 != EOF);
}
/* Read a 'C' comment from source file */
scomt () {
int c;
if ((c = sgetc ()) != '*') { /* Really a comment? */
ungetc (c, srcfile); /* No, put it back */
return;
}
sputc (c);
do {
while ((c = sputc (sgetc ())) != '*') { /* Find '*' */
if (c == EOF || c == CPMEOF) return;
if (c == '/') scomt (); /* Handle nested comments */
}
if ((c = sgetc ()) == '*') /* Handle ... ***/
ungetc (c, srcfile);
else sputc (c);
} while (c != '/' && c != EOF && c != CPMEOF);
}
/* Read a character from the stack of source files */
int sgetc () {
int c;
/* Open a file if necessary */
if (!sfopen) {
if (sopen () == EOF) return (EOF);
return (sgetc ());
}
c = getc (srcfile);
if (c == EOF || c == CPMEOF) {
fclose (srcfile);
sfopen = FALSE;
linage = 0;
return (sgetc ());
}
else return (c);
}
/* Open a source file */
int sopen () {
seof = TRUE;
if (nsrcfs-- <= 0) return (EOF);
if (fopen (*++sfiles, srcfile) == ERROR) {
fprintf (STD_ERR, "Can't open source file %s:%s\n",
*sfiles, errmsg (errno ()));
return (sopen ());
}
seof = FALSE;
sfopen = TRUE;
return (OK);
}
/* Copy a source character to the output file */
int sputc (c)
int c;
{
if (!slopen) { /* Begin a new comment line */
begline ();
printf ("\t\t");
}
if (c != EOF && c != CPMEOF && c != '\n' && c != '\r')
printf ("%c", c); /* Put the character */
if (c == '\n') slopen = FALSE; /* Close source line */
return (c);
}
/* Read a whitespace-terminated string from a file */
int frdstr (file, string)
char * file;
char * string;
{
int c;
if (skipwh (file) == ERROR) return (ERROR);
while ((c = kgetc (file)) != EOF && c != CPMEOF && !isspace (c))
*string++ = c;
*string++ = '\0';
return (OK);
}
/* Read a hex number from a file */
int frdhex (file, number)
char * file;
int * number;
{
int c;
int d;
if (skipwh (file) == ERROR) return (ERROR);
*number = 0;
while ((d = any ((c = toupper (kgetc (file))), "0123456789ABCDEF"))
!= -1) {
*number = (*number << 4) + d;
}
return (OK);
}
/* Skip whitespace on a file */
skipwh (file)
char * file;
{
int c;
while (isspace (c = kgetc (file)));
if (c == EOF || c == CPMEOF) return (ERROR);
ungetc (c, file);
return (OK);
}
/* Kludged 'getc' to make CR-LF pairs into '\n's */
int kgetc (file)
char * file;
{
int c;
if ((c = getc (file)) != '\r') return (c);
if ((c = getc (file)) == '\n') return (c);
ungetc (c, file);
return ('\r');
}
/* Begin a line on the listing */
begline () {
int ac;
char ** av;
printf ("\n");
/* Break page if necessary */
if (linage == 0 || linage >= pagelen) {
printf ("\fLine\t Times\tBDS `C' Execution Profile\t");
if (sfopen) printf ("%s", *sfiles);
printf ("\tPage %d\n", ++pageno);
printf ("----\t ----- < ");
for (ac=hisargc, av=hisargv; ac; --ac, ++av) {
printf ("%s ", *av);
}
printf (">\n\n");
linage = 3;
}
slopen = TRUE;
++linage;
}
/* Locate a character in a string */
int any (c, string)
char c;
char * string;
{
int i;
i = 0;
while (*string) if (c == *string++) return (i); else ++i;
return (-1);
}
/* Put an extension on a file name */
makeext (name, ext)
char * name; /* Name of the file */
char * ext; /* Desired extension */
{
do { /* Find the separatrix */
if (!*name) { /* No extension originally */
*name++ = '.';
break;
}
} while (*name++ != '.');
strcpy (name, ext); /* Add the extension */
return (name);
}
/* Seek on a CHARIO file with error checking */
tcseek (file, addr, flag, name)
char * file;
unsigned addr;
int flag;
char * name;
{
if (cseek (file, addr, flag) == ERROR) {
fprintf (STD_ERR, "Seek error on %s: %s.\n",
name, errmsg (errno ()));
quit ();
}
}
/* Read a CHARIO file with error checking */
tcread (file, buf, size, name)
char * file;
char * buf;
unsigned size;
char * name;
{
if (cread (file, buf, size) < size) {
fprintf (STD_ERR, "Read error on %s: %s.\n",
name, errmsg (errno ()));
quit ();
}
}
/* Abort the program */
quit () {
if (pass2) dioflush ();
exit ();
}
keext (name, ext)
char * name; /* Name of the file */
char *